﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.Remoting.Messaging;

namespace OperationPlugins.Validators
{
    /// <summary>
    /// A container for messages.
    /// </summary>
    [Serializable]
    public class MessageContainer : MarshalByRefObject, ILogicalThreadAffinative
    {
        #region Fields

        private readonly List<MessageContainer> _childContainers = new List<MessageContainer>();
        private readonly List<Message> _messages = new List<Message>();

        private const string ReadOnlyMessage = @"This ValidationResult object is readonly. Try to create a new ValidationResult object by using the default constructor.";

        #endregion

        /// <summary>
        /// Represents the success of the validation.
        /// </summary>
        public static readonly MessageContainer Success = new MessageContainer(true);

        /// <summary>
        /// Initializes a new instance of the MessageContainer class.
        /// </summary>
        public MessageContainer()
            : this(false)
        {
        }

        /// <summary>
        /// Initializes a new instance of the MessageContainer class.
        /// </summary>
        /// <param name="isReadonly">True if the MessageContainer object should be readonly, otherwise false.</param>
        protected MessageContainer(bool isReadonly)
        {
            IsReadonly = isReadonly;
        }

        /// <summary>
        /// Is this MessageContainer object readonly!
        /// </summary>
        protected bool IsReadonly { set; get; }

        /// <summary>
        /// Gets a Boolean value indicating whether this MessageContainer object or one of its child 
        /// MessageContainer objects contains any error messages.
        /// </summary>
        public virtual bool ContainsErrors
        {
            get { return _messages.Any(message => message.IsError) || _childContainers.Any(result => result.ContainsErrors); }
        }

        /// <summary>
        /// Gets a Boolean value indicating whether this MessageContainer object or one of its child 
        /// MessageContainer objects contains any notification messages.
        /// </summary>
        public virtual bool ContainsNotifications
        {
            get { return _messages.Any(message => message.IsNotification) || _childContainers.Any(result => result.ContainsNotifications); }
        }

        /// <summary>
        /// Gets all error messages.
        /// </summary>
        public virtual ReadOnlyCollection<Message> Errors
        {
            get
            {
                var l = new List<Message>();

                l.AddRange(_messages.Where(message => message.IsError));

                foreach (MessageContainer container in _childContainers)
                {
                    l.AddRange(container.Errors);
                }

                return l.AsReadOnly();
            }
        }

        /// <summary>
        /// Gets all notification messages.
        /// </summary>
        public virtual ReadOnlyCollection<Message> Notifications
        {
            get
            {
                var l = new List<Message>();

                l.AddRange(_messages.Where(message => message.IsNotification));

                foreach (MessageContainer container in _childContainers)
                {
                    l.AddRange(container.Notifications);
                }

                return l.AsReadOnly();
            }
        }

        /// <summary>
        /// Adds a message to this MessageContainer.
        /// </summary>
        /// <param name="message">The message to be added.</param>
        public virtual void Add(Message message)
        {
            if (message == null)
            {
                throw new ArgumentNullException("message");
            }

            if (IsReadonly)
            {
                throw new InvalidOperationException(ReadOnlyMessage);
            }

            _messages.Add(message);
        }

        /// <summary>
        /// Adds messages to this MessageContainer.
        /// </summary>
        /// <param name="container">The messages to be added.</param>
        public virtual void Add(MessageContainer container)
        {
            if (container == null)
            {
                throw new ArgumentNullException("container");
            }

            if (IsReadonly)
            {
                throw new InvalidOperationException(ReadOnlyMessage);
            }

            _childContainers.Add(container);
        }

        /// <summary>
        /// Clears the container.
        /// </summary>
        public virtual void Clear()
        {
            _childContainers.Clear();
            _messages.Clear();
        }

    }
}